package VSEChannel;
use strict;
use SOAP::Lite;
use Data::Dumper;
use Date::Manip;
use vars qw /@ISA/;
use DataCollectionChannel;
@ISA = ("DataCollectionChannel");

sub new{
	my $class = shift;
	my $self = {};
	$self->{STOCK_FIELD_FORMAT} = [ 'Symbol', 'RefPrice', 'CeilingPrice', 'FloorPrice',
									'Best1BidPrice', 'Best1BidVolume', 'Best2BidPrice', 'Best2BidVolume', 'Best3BidPrice', 'Best3BidVolume',
									'TempPrice', 'TempVolume', 'ChangedPrice', 
									'Best1OffPrice', 'Best1OffVolume', 'Best2OffPrice', 'Best2OffVolume', 'Best3OffPrice', 'Best3OffVolume',
									'SS1MatchPrice','HighestPrice', 'LowestPrice', 'FBuy', 'FRoom', 
									'Description', 'SS1MatchVolume', 'SS2MatchPrice', 'SS2MatchVolume' ];
									 
		
									
	$self->{MARKET_FIELD_FORMAT} = [ 'Index', 'ChangeIndex', 'TotalVolume', 'TotalValue', 'Advances', 
									 'NoChange', 'Declines', 'TradingDate', 'Time', 'SessionNo', 'NP_V', 'NV_V', 'PrevIndex', 'Transactions', 'ChangePercent' ];
	$self->{SECURITY_CENTER_CODE} = 'HoSE';
	$self->{URL} = 'http://www.hsx.vn/LS/HoSTC_Service.asmx?WSDL';
	bless $self, $class;
	return $self;	
}

sub get_stock_info_set{
	my $self = shift;
	my $fetcher;
	my $proc_time;
	my $start_time = time;
	my $channel_name = 	$self->{NAME};
	my $content_market_info;
	my $content_stock_info;
	my $content_stock_list;
	my %stock_desc_list;
	my $stock_info_set = StockInfoSet::new();
	
	# assign sys sequence and name to channelinfo object
	$stock_info_set->{CHANNEL_INFO}->set('SYS_SEQ', shift);
	$stock_info_set->{CHANNEL_INFO}->set('NAME', $channel_name);
	$stock_info_set->{CHANNEL_INFO}->set('START_TIME', $start_time);
	# assign priority
	$stock_info_set->{MARKET_INFO}->set('PRIORITY', $self->{PRIORITY});
		
	# get data source from url 
	while((time - $start_time) < $self->{TIMEOUT}){ # check time out
		$self->{STATUS}->{CODE} = 'CONNECTING';
		# check WSDL url
		eval{ $fetcher = SOAP::Lite
			 	->proxy('http://www.hsx.vn/LS/HoSTC_Service.asmx', timeout => $self->{TIMEOUT}, keep_alive => 1)
				->uri('http://tempuri.org')
				->on_action(sub{sprintf('%s/%s', @_);});
		};
		
		if ($@ || !ref($fetcher)){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Invalid WSDL address($@)";
			#print Dumper($self->{STATUS});
			next;	 
		}
		my $som;
		# fetch Total MKT and Security
		eval{ $som = $fetcher->GetLiveTotalMKT();};
		if($@){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $@;
			next;
		}
		if(!$som->fault){
			$content_market_info = $som->result();
			$self->{STATUS}->{CODE} = 'OK';
			$self->{STATUS}->{MSG} = "Data source is fetched";			
		}else{
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $som->faultcode . $som->faultstring;				
			next;
		}
		
		eval{$som = $fetcher->GetLiveSecurity();};
		if($@){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $@;
			next;
		}
		if(!$som->fault){
			$content_stock_info = $som->result();
			$self->{STATUS}->{CODE} = 'OK';
			$self->{STATUS}->{MSG} = "Data source is fetched";			
		}else{
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $som->faultcode . $som->faultstring;				
			next;
		}
		
		eval{$som = $fetcher->fGetSTOCKROOM();};
		if($@){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $@;
			next;
		}
		if(!$som->fault){
			$content_stock_list = $som->result();
			$self->{STATUS}->{CODE} = 'OK';
			$self->{STATUS}->{MSG} = "Data source is fetched";
			last;
		}else{
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $som->faultcode . $som->faultstring;				
			next;
		}
		
	}
	if ($self->{STATUS}->{CODE} ne 'OK'){
		#print Dumper($self->{STATUS});
		$self->{STATUS}->{CODE} = 'TIMEOUT';
		$self->{STATUS}->{MSG} = "Can\'t fetch data source(connection timeout)";
		$proc_time = time - $start_time;
		$stock_info_set->{CHANNEL_INFO}->set('PROCESSING_TIME', $proc_time);
		$stock_info_set->{CHANNEL_INFO}->set('STATUS', $self->{STATUS}->{CODE});
		$stock_info_set->{CHANNEL_INFO}->set('MESSAGE', $self->{STATUS}->{MSG});
		$stock_info_set->{MARKET_INFO} = undef;
		$stock_info_set->{STOCK_INFO_SET} = undef;		
		return ($stock_info_set,$self->{STATUS});		
	}
# parse stock description list
#	print $content_stock_list;
	my @stock_descs = split(/\*\*/, $content_stock_list);
	foreach my $stock_desc (@stock_descs){
		my @symbol_desc = split(/\|/, $stock_desc);
		$stock_desc_list{$symbol_desc[0]} = $symbol_desc[1];
	}
	
# parse $content_market_info
    #print $content_market_info;
	my @sessions = split(/\*\*/,$content_market_info);
	my @cur_session = split(/\|/, $sessions[@sessions-1]);
	
	#Remove comma from the digital format
	for(my $i =0; $i < @cur_session; $i++){
		if($cur_session[$i] =~ /[\d|,]*/){
			$cur_session[$i] =~ s/,//g; 
		}
	}
	
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[0], $cur_session[0]);	# Index
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[1], $cur_session[1]);	# ChangeIndex
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[2], $cur_session[4]);	# Total Volume
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[3], $cur_session[5]);	# Total Value
	
	# Fields that aren't contained in the data source are set to undef
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[4], undef);				# Advanced
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[5], undef);				# NoChange
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[6], undef);				# Decline
	
	# real date time processing
	$cur_session[6] =~ /(.*)\s(.*)/;
	my $date = $1;
	my $time = $2;
	my $time_string="00:00:00";
	if(length($time) == 6 ){
		$time_string = join(':',substr($time,0,2) ,substr($time,2,2),substr($time,4,2))
	}elsif(length($time) == 5){
		$time_string = join(':',substr($time,0,1) ,substr($time,1,2),substr($time,3,2))
	}
	
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[7], $date );				# Trading Date
	# set the market time based on the report from VSE
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[8], $time_string );				# Time
	# set market time based on the delay time
	if($self->{DELAY_TIME}){
		my $market_time = DateCalc(ParseDate("today"), "- " . $self->{DELAY_TIME} . " seconds");			
		$stock_info_set->{MARKET_INFO}->set('Time', UnixDate($market_time, "%H:%M:%S"));						
	}else{
		$stock_info_set->{MARKET_INFO}->set('Time', undef);			
	}
	
	# Fields that aren't contained in the data source are set to undef
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[9], undef);				# SessionNo
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[10], undef);				# NP_V
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[11], undef);				# NV_V
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[12], $cur_session[2]);		# Previous Index
	$stock_info_set->{MARKET_INFO}->set(@{$self->{MARKET_FIELD_FORMAT}}[13], $cur_session[3]);		# Number of Transactions
	$stock_info_set->{MARKET_INFO}->set('CHANNEL_NAME', $stock_info_set->{CHANNEL_INFO}->get('NAME'));
	$stock_info_set->{MARKET_INFO}->set('SYS_SEQ', $stock_info_set->{CHANNEL_INFO}->get('SYS_SEQ'));
	$stock_info_set	->{MARKET_INFO}->set('SECURITY_CENTER', $self->{SECURITY_CENTER_CODE});
	$stock_info_set	->{MARKET_INFO}->set('ChangePercent', sprintf("%.2f", 100* $stock_info_set->{MARKET_INFO}->get('ChangeIndex')/$stock_info_set->{MARKET_INFO}->get('PrevIndex')));
# parse $content_stock_info
	#print $content_stock_info;
	my @stocks = split(/\*\*/,$content_stock_info);
	foreach my $stock(@stocks){
		my @field_values = split(/\|/, $stock); 
		my $stock_info = StockInfo::new("ObjectInfo");
		for(my $i=0; $i<@{$self->{STOCK_FIELD_FORMAT}}; $i++){
			my $field = $self->{STOCK_FIELD_FORMAT}->[$i];
			if($i < @field_values){				
				$field_values[$i] =~ s/,//g;
				if($field =~ /.*Price/ ){
					$field_values[$i] = -1 if($field_values[$i] eq 'ATO');
					$field_values[$i] = -2 if($field_values[$i] eq 'ATC');
				}
				$stock_info->set($field, $field_values[$i]);
			}else{
				$stock_info->set($field, undef);
			}	
		}
		$stock_info->set('CHANNEL_NAME', $stock_info_set->{CHANNEL_INFO}->get('NAME'));
		$stock_info->set('SYS_SEQ', $stock_info_set->{CHANNEL_INFO}->get('SYS_SEQ'));	
		push @{$stock_info_set->{STOCK_INFO_SET}}, $stock_info;		
	}
	if(ref($stock_info_set->{MARKET_INFO}) && ref($stock_info_set->{STOCK_INFO_SET})){
		$self->{STATUS}->{CODE} = 'SUCCEEDED';
		$self->{STATUS}->{MSG} = 'The data source is parsed successfully';
		# calculate the processing time
	}else{
		$self->{STATUS}->{CODE} = 'FAILED';
		$self->{STATUS}->{MSG} = 'The data source isn\'t parsed sucessfully';
	}
	$proc_time = time - $start_time;	
	$stock_info_set->{CHANNEL_INFO}->set('PROCESSING_TIME', $proc_time);
	$stock_info_set->{CHANNEL_INFO}->set('STATUS', $self->{STATUS}->{CODE});
	$stock_info_set->{CHANNEL_INFO}->set('MESSAGE', $self->{STATUS}->{MSG});
			
	return ($stock_info_set,$self->{STATUS});				
}

##################################
## get stock info set			##
## input:						##
## output:						##
##		{HOUR => $hour 			##
##		 MIN => $time }			##
##################################
sub get_market_time{
	my $self = shift;
	my $fetcher;
	my $start_time = time;
	my $channel_name = 	$self->{NAME};
	my $content_market_info;
	# get data source from url 
	while((time - $start_time) < $self->{TIMEOUT}){ # check time out
		$self->{STATUS}->{CODE} = 'CONNECTING';
		# check WSDL url
		eval{ $fetcher = SOAP::Lite
			 	->proxy('http://www.hsx.vn/LS/HoSTC_Service.asmx', timeout => $self->{TIMEOUT}, keep_alive => 1)
				->uri('http://tempuri.org')
				->on_action(sub{sprintf('%s/%s', @_);});
		};
		if ($@ || !ref($fetcher)){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Invalid WSDL address($@)";
			#print Dumper($self->{STATUS});
			next;	 
		}
		my $som;
		# fetch Total MKT and Security
		eval{
			$som = $fetcher->GetLiveTotalMKT();	
		};
		if($@){
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $@;
			next;
		}
		if(!$som->fault){
			$content_market_info = $som->result();
			$self->{STATUS}->{CODE} = 'OK';
			$self->{STATUS}->{MSG} = "Data source is fetched";
			last;
		}else{
			$self->{STATUS}->{CODE} = 'RECONNECTING';
			$self->{STATUS}->{MSG} = "Can't fetch data source|" . $som->faultcode . $som->faultstring;				
			next;
		}		
	}
	if ($self->{STATUS}->{CODE} ne 'OK'){
		#print Dumper($self->{STATUS});
		$self->{STATUS}->{CODE} = 'TIMEOUT';
		$self->{STATUS}->{MSG} = "Can\'t fetch data source(connection timeout)";
		return (undef,$self->{STATUS});		
	}
	#print $content_market_info;
	my @sessions = split(/\*\*/,$content_market_info);
	my @cur_session = split(/\|/, $sessions[@sessions-1]);
	# get date time
	$cur_session[6] =~ /(.*)\s(.*)/;
	my $time = $2;
	if($time eq '0'){
		$self->{STATUS}->{CODE} = 'PENDING';
		$self->{STATUS}->{MSG} = "The market hasn't displayed(time data in an invalid format)";	
		return (undef, $self->{STATUS});
	}
		
	$self->{STATUS}->{CODE} = 'OK';
	$self->{STATUS}->{MSG} = "Time is fetched(market was openned)";
	if(length($time) == 5 ){
		return ({HOUR => substr($time,0,1), MIN => substr($time,1,2)}, $self->{STATUS});		
	}elsif(length($time) == 6){
		return ({HOUR => substr($time,0,2), MIN => substr($time,2,2)}, $self->{STATUS});
	}elsif(length($time) == 1){
		return ({HOUR => '00', MIN => '00'}, $self->{STATUS});
	}
	
	
}

#####################################
## get the market status 		   ##
#####################################

sub get_market_status{
	my $self = shift;
	my $market_start_time = shift;
	my $market_stop_time = shift;
	my $logger = shift;
	my $market_time;
	my $market_status = {
		CODE => "CLOSING",
		MSG => ""
	};
		
	# if the center is HoSE, check real time
	my ($market_time_tmp, $status) = $self->get_market_time();
	if(!ref($market_time_tmp)){
		return $status;
	}
	my $hour = $market_time_tmp->{HOUR};
	my $min = $market_time_tmp->{MIN};
	$market_time = ParseDate($hour . ':' . $min . ':00');			
	$market_status->{MSG} = 'Market time: ' . $market_time_tmp->{HOUR} . ':' . $market_time_tmp->{MIN} ;
	if(Date_Cmp($market_time, $market_start_time) >= 0 && Date_Cmp($market_time, $market_stop_time) <= 0){
		$market_status->{CODE} = 'OPENING';
	}else{
		$market_status->{CODE} = 'CLOSING';
	}
	return $market_status;
	
}

sub DESTROY{
	
}
1;